﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.Windows.Markup;
using System.IO;
using System.Xml;
using System.Windows;
using System.Windows.Media.Animation;
using System.Windows.Controls;
using System.Threading;
using System.Windows.Data;
using Structures;



namespace X3DXamlXna
{
    public class WorldInfo : Trans_WorldInfo
    {
        public string title { get; set; }
        public string info { get; set; }
    }

    public class Material : Mater_ImgText
    {
        public string shininess { get; set; }
        public string diffuseColor { get; set; }
        public string ambientIntensity { get; set; }
        public string containerField { get; set; }
        public string specularColor { get; set; }
        public string DEF { get; set; }
        public string USE { get; set; }
        public string emissiveColor { get; set; }
        public string transparency { get; set; }

    }

    public class ImageTexture : Mater_ImgText
    {
        public string DEF { get; set; }
        public string url { get; set; }
        public string repeatS { get; set; }
        public string repeatT { get; set; }
        public string containerField { get; set; }
    }

    public interface Mater_ImgText { }

    [ContentProperty("Mater_ImgText")]
    public class Appearance : Appear_IndexT_FaceSet
    {
        public List<Mater_ImgText> Mater_ImgText
        {
            get { return _Mater_ImgText; }
            set { _Mater_ImgText.Add(value.First()); }
        }
        public bool Touchable { get; set; }
        private List<Mater_ImgText> _Mater_ImgText = new List<Mater_ImgText>();
        public string containerField { get; set; }

        public Matrix World;
        public void inizialize(Matrix world, Game game)
        {
            World = world;
        }


    }
    public class Coordinate : Cor_Norm_Text
    {
        public string containerField { get; set; }
        public string point { get; set; }
        public string DEF { get; set; }
        public void inizialize(Matrix world, Game game) { }

        public string USE { get; set; }

    }
    public class TextureCoordinate : Cor_Norm_Text
    {
        public string containerField { get; set; }
        public string point { get; set; }
        public string DEF { get; set; }
        public void inizialize(Matrix world, Game game) { }

    }
    public class Normal : Cor_Norm_Text
    {
        public string containerField { get; set; }
        public string vector { get; set; }
        public string DEF { get; set; }
        public void inizialize(Matrix world, Game game) { }
    }


    public interface Cor_Norm_Text
    {
        void inizialize(Matrix world, Game game);
        string containerField { get; set; }
    }
    [ContentProperty("Cor_Norm_Text")]
    public class IndexedTriangleSet : Appear_IndexT_FaceSet
    {
        public List<Cor_Norm_Text> Cor_Norm_Text
        {
            get { return _Cor_Norm_Text; }
            set { _Cor_Norm_Text.Add(value.First()); }
        }
        public bool Touchable { get; set; }
        private List<Cor_Norm_Text> _Cor_Norm_Text = new List<Cor_Norm_Text>();
        public string solid { get; set; }
        public string index { get; set; }
        public string containerField { get; set; }

        public X3dModel Model { get; private set; }

        public Matrix World;
        public void inizialize(Matrix world, Game game)
        {
            World = world;

            string normals = "";
            string points = "";
            foreach (var indexTrielem in _Cor_Norm_Text)
            {
                if (indexTrielem is Coordinate)
                {
                    points = ((Coordinate)indexTrielem).point;
                }
                if (indexTrielem is Normal)
                {
                    normals = ((Normal)indexTrielem).vector;
                }
            }

            Model = new X3dModel();
            Model.World = World; 

            Model.setModelData(Utils.createArray(points, normals),game);
            Model.setModelIndices(game.GraphicsDevice, Utils.createIndices(index));

            Model.Touchable = Touchable;

            //ottengo il servizio per la suddivisione dello spazio e gli aggiungo il mio modello
            var model = (game.Services.GetService(typeof(ISpaceSubdivisionComponent)) as ISpaceSubdivisionComponent);
            model.models.Add(Model);
        }
    }

    [ContentProperty("Cor_Norm_Text")]
    public class IndexedFaceSet : Appear_IndexT_FaceSet
    {
        public List<Cor_Norm_Text> Cor_Norm_Text
        {
            get { return _Cor_Norm_Text; }
            set { _Cor_Norm_Text.Add(value.First()); }
        }
        private List<Cor_Norm_Text> _Cor_Norm_Text = new List<Cor_Norm_Text>();
        public bool Touchable { get; set; }

        public string DEF { get; set; }
        public string solid { get; set; }
        public string normalPerVertex { get; set; }
        public string normalIndex { get; set; }
        public string coordIndex { get; set; }
        public string containerField { get; set; }
        public string texCoordIndex { get; set; }
        public string creaseAngle { get; set; }
        public string ccw { get; set; }
        public string USE { get; set; }
        public X3dModel Model {get;set;}
        public Matrix World { get; set; }
        public void inizialize(Matrix world, Game game)
        {
            
            World = world;

            string normals = "";
            string points = "";
            foreach (var indexTrielem in _Cor_Norm_Text)
            {
                if (indexTrielem is Coordinate)
                {
                    points = ((Coordinate)indexTrielem).point;
                }
                if (indexTrielem is Normal)
                {
                    normals = ((Normal)indexTrielem).vector;
                }
            }

            Model = new X3dModel();
            Model.World = World;

            Model.setModelData(Utils.createArray(points, normals), game);
            Model.setModelIndices(game.GraphicsDevice, Utils.createIndices(coordIndex));

            Model.Touchable = Touchable;
            //ottengo il servizio per la suddivisione dello spazio e gli aggiungo il mio modello
            var model = (game.Services.GetService(typeof(ISpaceSubdivisionComponent)) as ISpaceSubdivisionComponent);
            model.models.Add(Model);
        }
    }

    public interface Appear_IndexT_FaceSet
    {
        bool Touchable { get; set; }
        string containerField { get; set; }
        void inizialize(Matrix world, Game game);
    }
    [ContentProperty("Appear_IndexT")]
    public class Shape : Shape_Time_Transform, Trans_WorldInfo
    {
        public List<Appear_IndexT_FaceSet> Appear_IndexT
        {
            get { return _Appear_IndexT_FaceSet; }
            set { _Appear_IndexT_FaceSet.Add(value.First()); }
        }
        public bool Touchable { get; set; }
        private List<Appear_IndexT_FaceSet> _Appear_IndexT_FaceSet = new List<Appear_IndexT_FaceSet>();
        public string containerField { get; set; }
        public string DEF { get; set; }

        public Matrix World { get; set; }

        public void initialize(Matrix world, Game game)
        {
            World = world;

            foreach (var child in _Appear_IndexT_FaceSet)
            {
                //controllo se tra i figli ho un touchsensor e se si
                if (Touchable)
                {
                    child.Touchable = true;
                    child.inizialize(World, game);
                }
                else
                {
                    child.inizialize(World, game);
                }
            }
        }

    }

    public class ProximitySensor : Shape_Time_Transform
    {
        public bool Touchable { get; set; }

        public string containerField { get; set; }
        public string size { get; set; }
        public string center { get; set; }
        public string DEF { get; set; }
        public string rotation { get; set; }
        public string scale { get; set; }
        public string translation { get; set; } 

        public Matrix World { get; set; }

        public void initialize(Matrix world, Game game)
        {

            var Rotation = Utils.setToVector4Rotation(rotation);
            var q = Quaternion.CreateFromAxisAngle(new Vector3(Rotation.X, Rotation.Y, Rotation.Z), Rotation.W);
            World =
                //Matrix.Invert(Matrix.CreateTranslation(Center)) *
                //Matrix.Invert(Matrix.CreateFromQuaternion(sr)) *
                  Matrix.CreateScale(Utils.setToVector3Scale(scale)) *
                //Matrix.CreateFromQuaternion(sr) *
                  Matrix.CreateFromQuaternion(q) *
                //Matrix.CreateTranslation(Center) *
                  Matrix.CreateTranslation(Utils.setToVector3Translation(translation)) * world;

            var dims = Utils.createVector(size) / 2.0f;

            var vertices = new Vector3[8];

            vertices[0] = new Vector3(-dims.X, -dims.Y, -dims.Z);
            vertices[1] = new Vector3(dims.X, -dims.Y, -dims.Z);
            vertices[2] = new Vector3(dims.X, dims.Y, -dims.Z);
            vertices[3] = new Vector3(-dims.X, dims.Y, -dims.Z);
            vertices[4] = new Vector3(-dims.X, -dims.Y, dims.Z);
            vertices[5] = new Vector3(dims.X, -dims.Y, dims.Z);
            vertices[6] = new Vector3(dims.X, dims.Y, dims.Z);
            vertices[7] = new Vector3(-dims.X, dims.Y, dims.Z);

            var trasf =
                (
                from v in vertices
                select Vector3.Transform(v, world)
                ).ToArray();

            var serV = game.Services.GetService(typeof(IProximityComponent)) as IProximityComponent;
            serV.Add(BoundingBox.CreateFromPoints(trasf));
        }

    }
    public class TimeSensor : Shape_Time_Transform
    {
        public bool Touchable { get; set; }
        public string DEF { get; set; }
        public string USE { get; set; }

        public string enabled
        {
            get { return myTimer.enabled.ToString(); }
            set { myTimer.enabled = bool.Parse(value); }
        }
        public string fraction_changed { get { return myTimer.fraction_changed.ToString(); } set { } }

        public string cycleInterval
        {
            get { return myTimer.cycleInterval.ToString(); }
            set { myTimer.cycleInterval = float.Parse(value); }
        }

        public string loop { get; set; }
        public string startTime { get; set; }
        public string stopTime { get; set; }
        public string pauseTime { get; set; }
        public string resumeTime { get; set; }
        public string cycleTime { get; set; }
        public string isActive { get; set; }
        public string isPaused { get; set; }
        public string time { get; set; }
        public string containerField { get; set; }

        private Timer myTimer;

        public void initialize(Matrix world, Game game)
        { }
    }

    public class TouchSensor : Shape_Time_Transform
    {
        public bool Touchable { get; set; }
        public string DEF { get; set; }
        public string containerField { get; set; }

        public void initialize(Matrix world, Game game)
        {
        }
    }

    public class SpotLight : Shape_Time_Transform
    {
        public bool Touchable { get; set; }
        public string DEF { get; set; }
        public string containerField { get; set; }
        public string ambientIntensity { get; set; }
        public string intensity { get; set; }
        public string radius { get; set; }
        public string cutOffAngle { get; set; }
        public string beamWidth { get; set; }
        public string attenuation { get; set; }
        public string color { get; set; }
        public string on { get; set; }
        public Matrix World { get; set; }

        public void initialize(Matrix world, Game game)
        {
            World = world;
        }
    }
    public class PointLight : Shape_Time_Transform
    {
        public bool Touchable { get; set; }
        public string DEF { get; set; }
        public string containerField { get; set; }
        public string ambientIntensity { get; set; }
        public string intensity { get; set; }
        public string radius { get; set; }
        public string cutOffAngle { get; set; }
        public string beamWidth { get; set; }
        public string attenuation { get; set; }
        public string color { get; set; }
        public string on { get; set; }

        public Matrix World { get; set; }
        public void initialize(Matrix world, Game game)
        {
            World = world;
        }
    }

    public class Viewpoint : Shape_Time_Transform, Trans_WorldInfo
    {
        #region X3d Tags
        public string DEF { get; set; }
        public string USE { get; set; }
        public string description { get; set; }
        public string position { get; set; }
        public string orientation { get; set; }
        public string fieldOfView { get; set; }
        public string jump { get; set; }
        public string centerOfRotation { get; set; }
        public string set_bind { get; set; }
        public string bindTime { get; set; }
        public string isBound { get; set; }
        public string containerField { get; set; }
        #endregion

        public bool Touchable { get; set; }

        public Matrix World { get; set; }
        public void initialize(Matrix world, Game game)
        {
            var cameras = (game.Services.GetService(typeof(ICameraService)) as ICameraService);

            float fov = float.Parse(fieldOfView, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture);

            var Rotation = Utils.setToVector4Rotation(orientation);
            var Translation = Utils.setToVector3Translation(position);

            var q = Quaternion.CreateFromAxisAngle(new Vector3(Rotation.X, Rotation.Y, Rotation.Z), Rotation.W);

            //var trans = Matrix.CreateFromQuaternion(q) * Matrix.CreateTranslation(Translation) * world;


            //var view = Matrix.CreateLookAt(trans.Translation, trans.Translation + trans.Forward, trans.Up);

            //Matrix cameraWorld = Matrix.CreateFromQuaternion(q);
            //cameraWorld.Translation = Vector3.Transform(Translation, world);
            //Matrix viewMatrix = Matrix.Invert(cameraWorld);

            //var r = viewMatrix == view;



            var nCamera = new Camera(Vector3.Transform(Translation, world), q, fov, game.GraphicsDevice.Viewport.AspectRatio);

            nCamera.DEF = DEF;
            nCamera.USE = USE;
            nCamera.description = description;
            try
            {
                nCamera.jump = bool.Parse(jump);
            }
            catch (Exception)
            {
                nCamera.jump = true;
            }
            nCamera.centerOfRotation = Utils.setToVector3Translation(centerOfRotation);
            try
            {
                nCamera.set_bind = bool.Parse(jump);
            }
            catch (Exception)
            {
                nCamera.set_bind = true;
            }
            try
            {
                nCamera.isBound = bool.Parse(jump);
            }
            catch (Exception)
            {
                nCamera.isBound = true;
            }
            //var nCamera = new Camera()
            //    {
            //        Name = DEF,
            //        View = viewMatrix,
            //        Projection = projection,
            //        FieldOfView = fov
            //    };

            cameras.AddViewPoint(nCamera);



            World = world;
        }

    }

    public interface Shape_Time_Transform
    {
        bool Touchable { get; set; }
        void initialize(Matrix world, Game game);
    }

    [ContentProperty("Shape_Time_Transform")]
    public class Transform : Trans_WorldInfo, Shape_Time_Transform
    {
        public List<Shape_Time_Transform> Shape_Time_Transform
        {
            get { return _Shape_Time_Transform; }
            set { _Shape_Time_Transform.Add(value.First()); }
        }
        private List<Shape_Time_Transform> _Shape_Time_Transform = new List<Shape_Time_Transform>();

        public bool Touchable { get; set; }
        public string containerField { get; set; }
        public string rotation { get; set; }
        public string translation { get; set; }
        public string DEF { get; set; }
        public string scale { get; set; }
        public string center { get; set; }
        public string scaleOrientation { get; set; }

        public Matrix World;
        public void initialize(Matrix world, Game game)
        {
            var Rotation = Utils.setToVector4Rotation(rotation);
            var q = Quaternion.CreateFromAxisAngle(new Vector3(Rotation.X, Rotation.Y, Rotation.Z), Rotation.W);
            World =
                //Matrix.Invert(Matrix.CreateTranslation(Center)) *
                //Matrix.Invert(Matrix.CreateFromQuaternion(sr)) *
                  Matrix.CreateScale(Utils.setToVector3Scale(scale)) *
                //Matrix.CreateFromQuaternion(sr) *
                  Matrix.CreateFromQuaternion(q) *
                //Matrix.CreateTranslation(Center) *
                  Matrix.CreateTranslation(Utils.setToVector3Translation(translation)) * world;
            if (Touchable == true)
                foreach (var child in _Shape_Time_Transform)
                {

                    child.Touchable = true;
                    child.initialize(World, game);
                }
            else
            {

                var touch = false;
                foreach (var child in _Shape_Time_Transform)
                {
                    if (child is TouchSensor)
                    {
                        touch = true;
                        break;
                    }
                }
                foreach (var child in _Shape_Time_Transform)
                {
                    //controllo se tra i figli ho un touchsensor e se si
                    if (touch)
                    {
                        child.Touchable = true;
                    }
                    child.initialize(World, game);           
                }
            }

        }

    }


    [ContentProperty("Shape_Time_Transform")]
    public class Group : Trans_WorldInfo, Shape_Time_Transform
    {
        public List<Shape_Time_Transform> Shape_Time_Transform
        {
            get { return _Shape_Time_Transform; }
            set { _Shape_Time_Transform.Add(value.First()); }
        }
        private List<Shape_Time_Transform> _Shape_Time_Transform = new List<Shape_Time_Transform>();

        public bool Touchable { get; set; }
        public string DEF { get; set; }

        public void initialize(Matrix world, Game game)
        {
            if (Touchable == true)
                foreach (var child in _Shape_Time_Transform)
                {

                    child.Touchable = true;
                    child.initialize(world, game);
                }
            else
            {

                var touch = false;
                foreach (var child in _Shape_Time_Transform)
                {
                    if (child is TouchSensor)
                    {
                        touch = true;
                        break;
                    }
                }
                foreach (var child in _Shape_Time_Transform)
                {
                    //controllo se tra i figli ho un touchsensor e se si
                    if (touch)
                    {
                        child.Touchable = true;
                    }
                    child.initialize(world, game);
                }
            }

        }

    }


    public class ROUTE : Trans_WorldInfo
    {
        public string fromNode { get; set; }
        public string fromField { get; set; }
        public string toNode { get; set; }
        public string toField { get; set; }
    }
    public class PositionInterpolator : Trans_WorldInfo
    {
        public string DEF { get; set; }
        public string USE { get; set; }
        public string key { get; set; }
        public string keyValue { get; set; }
        public string set_fraction { get; set; }
        public string value_changed { get; set; }
        public string containerField { get; set; }        
    }
    public class OrientationInterpolator : Trans_WorldInfo
    {
        public string DEF { get; set; }
        public string USE { get; set; }
        public string key { get; set; }
        public string keyValue { get; set; }
        public string set_fraction { get; set; }
        public string value_changed { get; set; }
        public string containerField { get; set; }
    }
    public interface Trans_WorldInfo { }
    [ContentProperty("Trans_Inf")]
    public class Scene : Global
    {
        public List<Trans_WorldInfo> Trans_Inf
        {
            get { return _Trans_Inf; }
            set { _Trans_Inf.Add(value.First()); }
        }
        private List<Trans_WorldInfo> _Trans_Inf = new List<Trans_WorldInfo>();
        public void initialize(Game game)
        {
            foreach (var child in _Trans_Inf)
            {
                if (child is Shape_Time_Transform)
                {
                    (child as Shape_Time_Transform).initialize(Matrix.Identity, game);
                    continue;
                }
            }
        }

    }

    public interface Global { }

    [ContentProperty("Global")]
    public class X3D
    {
        public string profile { get; set; }

        public List<Global> Global
        {
            get { return _Global; }
            set { _Global.Add(value.First()); }
        }
        private List<Global> _Global = new List<Global>();

        public void initialize(Game game)
        {
            foreach (var el in Global)
            {
                if (el is Scene)
                {
                    (el as Scene).initialize(game);
                }
            }

        }
    }


    [ContentProperty("meta")]
    public class head : Global
    {
        public List<meta> meta
        {
            get { return _meta; }
            set { _meta.Add(value.First()); }
        }
        private List<meta> _meta = new List<meta>();
    }

    public class meta
    {
        public string name { get; set; }
        public string content { get; set; }
    }



}